﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.FSharp.Collections;
using Microsoft.FSharp.Core;
using Microsoft.Glee.Drawing;

namespace CoreReset {

    /// <summary>
    /// Class CommandGraphForm is the form used for textual/graphical display of
    /// Context Graphs / DFRs which are computed for a certain command.
    /// </summary>
    public partial class CommandGraphForm : Form {
        private readonly MainForm m_mainForm;

        public CommandGraphForm(MainForm mainForm, string text, Dictionary<ContextGraph.Context, Dictionary<ContextGraph.Context, FSharpRef<setOfPairs.DFR>>> graph) {
            InitializeComponent();
            m_mainForm = mainForm;
            SetText(text);
            SetGraph(graph);
        }

        public void SetText(string text) {
            if (text != null) {
                textBoxCommandGraph.Lines = text.Split('\n');
            }
        }

        private void CommandGraphForm_FormClosing(object sender, FormClosingEventArgs e) {
            m_mainForm.OnCommandGraphFormClosing();
        }

        private static string context2text(FSharpList<int> c) {
            string txt = "{ ";
            for (int i = 0; i < c.Length; ++i) {
                txt += Parser.parserLastVarToIndexMap()[c[i]] + ((i < c.Length - 1) ? ", " : "");
            }
            txt += " }";
            return txt;
        }

        private static Edge AddEdge(Graph g, String pre, String post, Tuple<Graph, Graph> dfr) {
            string preText = pre;
            string postText = post + "'";
            var e = g.AddEdge(preText, postText);
            e.UserData = dfr;
            if (dfr != null) {
                dfr.Item1.GraphAttr.LayerDirection = LayerDirection.LR;
                dfr.Item2.GraphAttr.LayerDirection = LayerDirection.LR;
            }
            var p = e.SourceNode;
            var q = e.TargetNode;
            p.Attr.Shape = Shape.Plaintext;
            q.Attr.Shape = Shape.Plaintext;
            p.Attr.Color = Color.Red;
            q.Attr.Color = Color.DarkGreen;
            return e;
        }

        private static Edge AddEdge(Graph g, FSharpList<int> pre, FSharpList<int> post, Tuple<Graph, Graph> dfr) {
            string preText = context2text(pre);
            string postText = context2text(post) + "'";
            var e = g.AddEdge(preText, postText);
            e.UserData = dfr;
            if (dfr != null) {
                dfr.Item1.GraphAttr.LayerDirection = LayerDirection.LR;
                dfr.Item2.GraphAttr.LayerDirection = LayerDirection.LR;
            }
            var p = e.SourceNode;
            var q = e.TargetNode;
            p.Attr.Shape = Shape.Plaintext;
            q.Attr.Shape = Shape.Plaintext;
            p.Attr.Color = Color.Red;
            q.Attr.Color = Color.DarkGreen;
            return e;
        }

        private static Edge AddEdge(Graph g, FSharpList<int> preReset, FSharpList<int> preConst, FSharpList<int> postReset, FSharpList<int> postConst, Tuple<Graph, Graph> dfr) {
            var preResetText = "R: " + context2text(preReset);
            var postResetText = "R: " + context2text(postReset) + "' ";

            var preConstText = "C: " + context2text(preConst);
            var postConstText = "C: " + context2text(postConst) + "'";

            var e = g.AddEdge(preResetText + preConstText, postResetText + postConstText);
            e.UserData = dfr;
            if (dfr != null) {
                dfr.Item1.GraphAttr.LayerDirection = LayerDirection.LR;
                dfr.Item2.GraphAttr.LayerDirection = LayerDirection.LR;
            }
            var p = e.SourceNode;
            var q = e.TargetNode;
            p.Attr.Shape = Shape.Plaintext;
            q.Attr.Shape = Shape.Plaintext;
            p.Attr.Color = Color.Red;
            q.Attr.Color = Color.DarkGreen;
            return e;
        }

        private string Pat2ElaboratedStr(Pattern.Pat pat, int indent) {
            string spaces = "";
            for (int i = 0; i < indent; ++i)
                spaces += " ";
            if (pat == null)
                return spaces;
            if (pat.IsCmd) {
                var cmd = pat as Pattern.Pat.Cmd;
                string suffix = "";
                int childNum = m_mainForm.ChildNumberUnderBranch(cmd.Item);
                if (childNum > 0) {
                    string num = (childNum == 1) ? "first" : "second";
                    suffix = "    (taking the " + num + " branch)";
                }
                return spaces + m_mainForm.GetCommandText(cmd.Item) + suffix;
            }
            if (pat.IsSeq) {
                var seq = pat as Pattern.Pat.Seq;
                return Pat2ElaboratedStr(seq.Item1, indent) + ";\n" + Pat2ElaboratedStr(seq.Item2, indent);
            }
            if (pat.IsLoop) {
                var l = pat as Pattern.Pat.Loop;
                return spaces + "loop (" + l.Item1.ToString() + ") {\n" + Pat2ElaboratedStr(l.Item2, indent + 4) + "\n" + spaces + "}";
            }
            if (pat.IsPow) {
                var pow = pat as Pattern.Pat.Pow;
                return spaces + pow.Item2.ToString() + " * {\n" + Pat2ElaboratedStr(pow.Item1, indent + 4) + "\n" + spaces + "}";
            }

            return spaces;
        }

        private void AddVarEdge(Graph g, int src, int tgt, string tag, Pattern.Pat pat) {
            string var = Parser.parserLastVarToIndexMap()[src];
            var e = g.AddEdge(var, tag, var + "'");
            e.SourceNode.Attr.Shape = Shape.Plaintext;
            e.TargetNode.Attr.Shape = Shape.Plaintext;
            e.UserData = Pat2ElaboratedStr(pat, 0);
        }

        private static int dEdgeCount;

        private void AddDoubleVarEdge(Graph g, int i, int j, int i1, int j1) {
            dEdgeCount++;
            string srcId = dEdgeCount.ToString();
            Node src = g.AddNode(srcId);

            dEdgeCount++;
            string tgtId = dEdgeCount.ToString();
            Node tgt = g.AddNode(tgtId);
            src.Attr.Label = string.Format("{0}\n{1}", Parser.parserLastVarToIndexMap()[i], Parser.parserLastVarToIndexMap()[i1]);
            tgt.Attr.Label = string.Format("{0}'\n{1}'", Parser.parserLastVarToIndexMap()[j], Parser.parserLastVarToIndexMap()[j1]);
            Edge e = g.AddEdge(srcId, tgtId);
            e.SourceNode.Attr.Shape = Shape.Plaintext;
            e.TargetNode.Attr.Shape = Shape.Plaintext;
            e.Attr.Color = Color.Blue;
            e = g.AddEdge(srcId, tgtId);
            e.Attr.Color = Color.Blue;
        }

        private string ComputeEdgeTag(AbstractDomain.abstractValue abstractValue) {
            string tag = "unknown";
            if (abstractValue.Item == AbstractDomain.none.Item)
                tag = "none";
            else if (abstractValue.Item == AbstractDomain.copy.Item)
                tag = "copy";
            else if (abstractValue.Item == AbstractDomain.additive.Item)
                tag = "additive";
            else if (abstractValue.Item == AbstractDomain.polynomial.Item)
                tag = "polynomial";
            else if (abstractValue.Item == AbstractDomain.exponentialAtLeast.Item)
                tag = "exponentialAtLeast";

            return tag;
        }

        internal void SetGraph(Dictionary<ContextGraph.Context, Dictionary<ContextGraph.Context, FSharpRef<setOfPairs.DFR>>> graph) {
            if (graph == null) return;

            var g = new Graph("Contexts") { GraphAttr = { LayerDirection = LayerDirection.LR } };
            Tuple<Graph, Graph> lastDfr = null;
            Edge lastEdge2 = null;
            Edge lastEdge = null;
            foreach (ContextGraph.edge.E e in ContextGraph.getEdges(graph)) {
                var preReset = ContextGraph.resetContextToVarList(e.Item1);
                var postReset = ContextGraph.resetContextToVarList(e.Item2);
                var preConstant = ContextGraph.ConstantContextToVarList(e.Item1);
                var postConstant = ContextGraph.ConstantContextToVarList(e.Item2);
                var m = e.Item3.Value.Item1;
                var r = e.Item3.Value.Item2;
                var dfr = new Graph("DFR");
                foreach (var e1 in m.Item) {
                    var u = e1.Item1;
                    var v = e1.Item2;
                    var tag = ComputeEdgeTag(e1.Item3);
                    AddVarEdge(dfr, u, v, tag, e1.Item4);
                }
                var rg = new Graph("R");
                foreach (var e1 in r) {
                    int i = e1.Item1.Item1;
                    int j = e1.Item1.Item2;
                    int ii = e1.Item2.Item1;
                    int jj = e1.Item2.Item2;
                    AddDoubleVarEdge(rg, i, j, ii, jj);
                }

                var mr = new Tuple<Graph, Graph>(dfr, rg);
                lastEdge = AddEdge(g, preReset, preConstant, postReset, postConstant, mr);
                lastDfr = mr;
            }

            gViewerCG.Graph = g;
            if (lastDfr != null) {
                gViewerDFR.Graph = lastDfr.Item1;
                gViewerR.Graph = lastDfr.Item2;
            } else {
                gViewerDFR.Graph = null;
                gViewerR.Graph = null;
            }
            if (lastEdge != null) {
                selectedObjectAttr = lastEdge.Attr.Clone();
                lastEdge.Attr.Color = Color.Magenta;
                lastEdge.Attr.Fontcolor = Color.Magenta;
                selectedObject = lastEdge;
            }

            tabPage2.Invalidate();
        }

        private void gViewer_SelectionChanged(object sender, EventArgs e) {
            var edge = gViewerDFR.SelectedObject as Edge;
            if (edge != null) {
                if (selectedObject != null) {
                    if (selectedObject is Edge)
                        (selectedObject as Edge).Attr = selectedObjectAttr as EdgeAttr;
                }
                selectedObject = gViewerCG.SelectedObject;

                if (selectedObject is Edge) {
                    selectedObjectAttr = (gViewerCG.SelectedObject as Edge).Attr.Clone();
                    (gViewerCG.SelectedObject as Edge).Attr.Color = Color.Magenta;
                    (gViewerCG.SelectedObject as Edge).Attr.Fontcolor = Color.Magenta;
                    var dfr = edge.UserData as Tuple<Graph, Graph>;
                    gViewerDFR.Graph = dfr.Item1;
                    gViewerR.Graph = dfr.Item2;
                    gViewerCG.AutoSize = false;
                }
            }
            gViewerCG.Invalidate();
        }

        private object selectedObjectAttr;
        private object selectedObject;

        private object selectedDFRObjectAttr;
        private object selectedDFRObject;

        private void gViewerDFR_SelectionChanged(object sender, EventArgs e) {
            var edge = gViewerDFR.SelectedObject as Edge;
            if (edge != null) {
                if (selectedDFRObject is Edge)
                    (selectedDFRObject as Edge).Attr = selectedDFRObjectAttr as EdgeAttr;
                selectedDFRObject = gViewerDFR.SelectedObject;
                selectedDFRObjectAttr = (gViewerDFR.SelectedObject as Edge).Attr.Clone();
                (gViewerDFR.SelectedObject as Edge).Attr.Color = Color.Magenta;
                (gViewerDFR.SelectedObject as Edge).Attr.Fontcolor = Color.Magenta;
                var pattern = edge.UserData as string;
                textBoxPattern.Text = pattern;
                gViewerDFR.AutoSize = false;
            }
            gViewerDFR.Invalidate();
        }

        private void gViewerDFR_MouseDoubleClick(object sender, MouseEventArgs e) {
            var edge = gViewerDFR.SelectedObject as Edge;
            if (edge == null)
                return;

            var pattern = (string)edge.UserData;
            var frm = new DebugLogForm(null, "Computational Path");
            frm.AddText(pattern);
            frm.Show();
        }
    }
}